﻿// (c) Copyright Fernando Cerqueira.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using System.Windows.Shapes;
using ResApp = DevPhone.Controls.Properties;

// ReSharper disable CheckNamespace
namespace DevPhone.Controls
// ReSharper restore CheckNamespace
{
    [TemplateVisualState(Name = NormalState, GroupName = CommonStates)]
    [TemplateVisualState(Name = DisabledState, GroupName = CommonStates)]
    [TemplatePart(Name = "Header", Type = typeof(ContentControl))]
    [TemplatePart(Name = "ValueArea", Type = typeof(StackPanel))]
    [TemplatePart(Name = "ArrowArea", Type = typeof(StackPanel))]
    public class UpDownControl : Control
    {
        //flag loaded contrlol
        private bool _isloadedTemplate;

        private static SolidColorBrush _defaultArrowColor;
        private static SolidColorBrush _defaultArrowDisabledColor;
        private static readonly Brush DefautBackground = new SolidColorBrush(Color.FromArgb(01, 255, 255, 255));


        private DispatcherTimer _timer;
        private Button _bntLeft;
        private Button _bntright;
        private Path _arrLeft;
        private Path _arrright;
        private TextBlock _textValue;
        private TextBlock _textAfter;

        private int _countTick;
        private bool _leftHold;
        private bool _rightHold;

        private const string DownArrow = "M0,0 L1,0 0.5,1Z";
        private const string UpArrow = "M0,1 L1,1 0.5,0Z";
        private const string RightArrow = "M0,0 L1,0.5 0,1Z";
        private const string LeftArrow = "M0,0.5 L1,1 1,0Z";

        public event EventHandler<UpDownCustomValueChangedEventArgs> UpDownValueChanged;

        public UpDownControl()
        {
            DefaultStyleKey = typeof(UpDownControl);

            if (_defaultArrowColor == null || _defaultArrowDisabledColor == null)
            {
                _defaultArrowColor = new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"]);
                _defaultArrowDisabledColor = new SolidColorBrush((Color)Application.Current.Resources["PhoneDisabledColor"]);
            }
        }

        private void DoWorkOnRender(object sender, EventArgs args)
        {
            CompositionTarget.Rendering -= DoWorkOnRender;
            if (CurrentValue >= MaxValue)
            {
                _arrright.Fill = _defaultArrowDisabledColor;
                _bntright.IsEnabled = false;
            }
            else
            {
                _arrright.Fill = ArrowColor;
                _bntright.IsEnabled = true;
            }
            if (CurrentValue <= MinValue)
            {
                _arrLeft.Fill = _defaultArrowDisabledColor;
                _bntLeft.IsEnabled = false;
            }
            else
            {
                _arrLeft.Fill = ArrowColor;
                _bntLeft.IsEnabled = true;
            }
            _textValue.Text = CurrentValue.ToString(CultureInfo.InvariantCulture);
            OnChangedValue(CurrentValue, CurrentValue, CurrentValue.ToString(CultureInfo.InvariantCulture), TextAfterValue);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _textValue = GetTemplateChild("TextValue") as TextBlock;
            _textAfter = GetTemplateChild("TextInfo") as TextBlock;

            _arrright = GetTemplateChild("ArrowRight") as Path;
            _arrLeft = GetTemplateChild("ArrowLeft") as Path;

            _bntLeft = GetTemplateChild("BntLeft") as Button;
            _bntright = GetTemplateChild("BntRight") as Button;

            if (_bntLeft != null)
            {
                _bntLeft.Click += BntLeftClick;
                _bntLeft.Hold += BntLeftHold;
            }

            if (_bntright != null)
            {
                _bntright.Click += BntRightClick;
                _bntright.Hold += BntRightHold;
            }

            if (ArrowColor == null)
            {
                ArrowColor = _defaultArrowColor;
            }

            if (ArrowDisabledColor == null)
            {
                ArrowDisabledColor = _defaultArrowDisabledColor;
            }

            if (_arrLeft != null && _arrright != null)
            {
                _arrLeft.Fill = ArrowColor;
                _arrright.Fill = ArrowColor;

                switch (ArrowDirection)
                {
                    case UpDownArrowType.LeftRight:
                        {
                            _arrLeft.Data = StringPathGeometry.Convert(LeftArrow);
                            _arrright.Data = StringPathGeometry.Convert(RightArrow);
                        }
                        break;
                    case UpDownArrowType.UpDown:
                        {
                            _arrLeft.Data = StringPathGeometry.Convert(DownArrow);
                            _arrright.Data = StringPathGeometry.Convert(UpArrow);
                        }
                        break;
                    case UpDownArrowType.External:
                        {
                            if (string.IsNullOrEmpty(ExternalPathLeft))
                            {
                                throw new InvalidOperationException("External path left is null or empty.");
                            }
                            if (string.IsNullOrEmpty(ExternalPathRight))
                            {
                                throw new InvalidOperationException("External path right is null or empty.");
                            }
                            _arrLeft.Data = StringPathGeometry.Convert(ExternalPathLeft);
                            _arrright.Data = StringPathGeometry.Convert(ExternalPathRight);
                        }
                        break;
                }
            }

            IsEnabledChanged += delegate
            {
                ChangeVisualState(true);
            };
            ChangeVisualState(false);

            // Translate it into view once layout has been updated
            Dispatcher.BeginInvoke(() =>
            {
                if (MinValue > MaxValue)
                {
                    MinValue = MaxValue;
                }
                if (MaxValue < MinValue)
                {
                    MaxValue = MinValue;
                }
                if (CurrentValue > MaxValue)
                {
                    SetValue(CurrentValueProperty, MaxValue);
                }
                if (CurrentValue < MinValue)
                {
                    SetValue(CurrentValueProperty, MinValue);
                }

                _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(SpeedHold) };
                _timer.Tick += TimerTick;

                CompositionTarget.Rendering += DoWorkOnRender;

                _isloadedTemplate = true;
            });
        }

        private void ChangeVisualState(bool useTransitions)
        {
            if (_isloadedTemplate)
            {
                SetStateAndColorArrows(CurrentValue);
                VisualStateManager.GoToState(this, IsEnabled ? NormalState : DisabledState, useTransitions);
            }
        }

        private void OnChangedValue(double oldvalue, double newvalue, string customvalue, string aftertext)
        {
            if (UpDownValueChanged != null)
            {
                var tmp = UpDownValueChanged;
                var arg = new UpDownCustomValueChangedEventArgs(oldvalue, newvalue, customvalue, aftertext);
                tmp(this, arg);
                CurrentValue = arg.NewValue;
                _textValue.Text = arg.CustomValue;
                _textAfter.Text = arg.CustomText;
            }
            else
            {
                _textValue.Text = newvalue.ToString(CultureInfo.InvariantCulture);
                _textAfter.Text = aftertext;
            }
        }

        private void TimerTick(object sender, EventArgs e)
        {
            _countTick++;
            var inc = Increment;
            if (_leftHold)
            {
                if (_countTick > IncrementHoldStart)
                {
                    inc = IncrementHold;
                }
                var value = CurrentValue;
                value -= inc;
                if (value <= MinValue)
                {
                    if (LoopValues)
                    {
                        value = MaxValue;
                    }
                    else
                    {
                        value = MinValue;
                        _leftHold = false;
                        _countTick = 0;
                        _timer.Stop();
                    }
                }
                CurrentValue = value;
            }
            else if (_rightHold)
            {
                if (_countTick > IncrementHoldStart)
                {
                    inc = IncrementHold;
                }
                var value = CurrentValue;
                value += inc;
                if (value > MaxValue)
                {
                    if (LoopValues)
                    {
                        value = MinValue;
                    }
                    else
                    {
                        value = MaxValue;
                        _rightHold = false;
                        _countTick = 0;
                        _timer.Stop();
                    }
                }
                CurrentValue = value;
            }
        }

        private void BntLeftClick(object sender, RoutedEventArgs e)
        {
            if (_timer.IsEnabled)
            {
                _countTick = 0;
                _timer.Stop();
                return;
            }

            var value = CurrentValue;
            value -= Increment;
            if (value <= MinValue)
            {
                value = LoopValues ? MaxValue : MinValue;
            }
            CurrentValue = value;

        }

        private void BntRightClick(object sender, RoutedEventArgs e)
        {
            if (_timer.IsEnabled)
            {
                _countTick = 0;
                _timer.Stop();
                return;
            }
            var value = CurrentValue;
            value += Increment;
            if (value >= MaxValue)
            {
                value = LoopValues ? MinValue : MaxValue;
            }
            CurrentValue = value;
        }

        private void BntLeftHold(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _timer.Stop();
            _leftHold = true;
            _rightHold = false;
            _countTick = 0;
            _timer.Start();
        }

        private void BntRightHold(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _timer.Stop();
            _leftHold = false;
            _rightHold = true;
            _countTick = 0;
            _timer.Start();
        }

        private const string CommonStates = "CommonStates";

        private const string NormalState = "Normal";

        private const string DisabledState = "Disabled";

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(object), typeof(UpDownControl), new PropertyMetadata(null));

        public object Header
        {
            get { return GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty HeaderTemplateProperty =
            DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(UpDownControl), new PropertyMetadata(null));

        public DataTemplate HeaderTemplate
        {
            get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
            set { SetValue(HeaderTemplateProperty, value); }
        }

        public static readonly DependencyProperty ArrowColorProperty =
            DependencyProperty.Register("ArrowColor", typeof(Brush), typeof(UpDownControl), new PropertyMetadata(null, ArrowColorChangedCallback));

        private static void ArrowColorChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (UpDownControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.ArrowColor = _defaultArrowColor;
            }
            if (instance._isloadedTemplate)
            {
                instance.SetStateAndColorArrows(instance.CurrentValue);
            }
        }

        public Brush ArrowColor
        {
            get { return (Brush)GetValue(ArrowColorProperty); }
            set { SetValue(ArrowColorProperty, value); }
        }

        public static readonly DependencyProperty ArrowDirectionProperty =
            DependencyProperty.Register("ArrowDirection", typeof(UpDownArrowType), typeof(UpDownControl),
                new PropertyMetadata(UpDownArrowType.LeftRight, null));

        public UpDownArrowType ArrowDirection
        {
            get { return (UpDownArrowType)GetValue(ArrowDirectionProperty); }
            set { SetValue(ArrowDirectionProperty, value); }
        }

        public static readonly DependencyProperty ArrowDisabledColorProperty =
            DependencyProperty.Register("ArrowDisabledColor", typeof(Brush), typeof(UpDownControl), new PropertyMetadata(null, ArrowDisabledChangedCallback));

        private static void ArrowDisabledChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (UpDownControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.ArrowDisabledColor = _defaultArrowDisabledColor;
            }
            if (instance._isloadedTemplate)
            {
                instance.SetStateAndColorArrows(instance.CurrentValue);
            }
        }

        public Brush ArrowDisabledColor
        {
            get { return (Brush)GetValue(ArrowDisabledColorProperty); }
            set { SetValue(ArrowDisabledColorProperty, value); }
        }

        public static readonly DependencyProperty GapArrowProperty =
                DependencyProperty.Register("GapArrow", typeof(double), typeof(UpDownControl),
                                new PropertyMetadata(double.Parse("30", CultureInfo.InvariantCulture), null));

        public double GapArrow
        {
            get { return (double)GetValue(GapArrowProperty); }
            set { SetValue(GapArrowProperty, value); }
        }

        public static readonly DependencyProperty MinValueProperty =
            DependencyProperty.Register("MinValue", typeof(double), typeof(UpDownControl),
                                new PropertyMetadata(double.Parse("0", CultureInfo.InvariantCulture), MinValueChangedCallback));

        private static void MinValueChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (UpDownControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance.SetStateAndColorArrows(instance.CurrentValue);
            }
        }

        public double MinValue
        {
            get { return (double)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }

        public static readonly DependencyProperty MaxValueProperty =
            DependencyProperty.Register("MaxValue", typeof(double), typeof(UpDownControl),
                                        new PropertyMetadata(double.Parse("0", CultureInfo.InvariantCulture), MaxValueChangedCallback));

        private static void MaxValueChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (UpDownControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance.SetStateAndColorArrows(instance.CurrentValue);
            }
        }

        public double MaxValue
        {
            get { return (double)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }

        public static readonly DependencyProperty SpeedHoldProperty =
            DependencyProperty.Register("SpeedHold", typeof(int), typeof(UpDownControl),
                                new PropertyMetadata(int.Parse("250", CultureInfo.InvariantCulture), null));

        public int SpeedHold
        {
            get { return (int)GetValue(SpeedHoldProperty); }
            set
            {
                // ReSharper disable LocalizableElement
                if (value < 100)
                {
                    throw new ArgumentOutOfRangeException("value", "SpeedHold value must be between 100 and 500.");
                }
                if (value > 500)
                {
                    throw new ArgumentOutOfRangeException("value", "SpeedHold value must be between 100 and 500.");
                }
                // ReSharper restore LocalizableElement
                SetValue(SpeedHoldProperty, value);
            }
        }

        public static readonly DependencyProperty ExternalPathLeftProperty =
            DependencyProperty.Register("ExternalPathLeft", typeof(string), typeof(UpDownControl),
                                new PropertyMetadata(default(string), null));

        public string ExternalPathLeft
        {
            get { return (string)GetValue(ExternalPathLeftProperty); }
            set { SetValue(ExternalPathLeftProperty, value); }
        }

        public static readonly DependencyProperty ExternalPathRightProperty =
            DependencyProperty.Register("ExternalPathRight", typeof(string), typeof(UpDownControl),
                                new PropertyMetadata(default(string), null));

        public string ExternalPathRight
        {
            get { return (string)GetValue(ExternalPathRightProperty); }
            set { SetValue(ExternalPathRightProperty, value); }
        }

        public static readonly DependencyProperty IncrementHoldStartProperty =
            DependencyProperty.Register("IncrementHoldStart", typeof(int), typeof(UpDownControl),
                                new PropertyMetadata(int.Parse("5", CultureInfo.InvariantCulture), null));

        public int IncrementHoldStart
        {
            get { return (int)GetValue(IncrementHoldStartProperty); }
            set { SetValue(IncrementHoldStartProperty, value); }
        }

        public static readonly DependencyProperty IncrementHoldProperty =
            DependencyProperty.Register("IncrementHold", typeof(double), typeof(UpDownControl),
                                        new PropertyMetadata(double.Parse("0", CultureInfo.InvariantCulture), null));

        public double IncrementHold
        {
            get { return (double)GetValue(IncrementHoldProperty); }
            set { SetValue(IncrementHoldProperty, value); }
        }

        public static readonly DependencyProperty IncrementProperty =
            DependencyProperty.Register("Increment", typeof(double), typeof(UpDownControl),
                                        new PropertyMetadata(double.Parse("0", CultureInfo.InvariantCulture), null));

        public double Increment
        {
            get { return (double)GetValue(IncrementProperty); }
            set { SetValue(IncrementProperty, value); }
        }

        public static readonly DependencyProperty CurrentValueProperty =
            DependencyProperty.Register("CurrentValue", typeof(double), typeof(UpDownControl),
                                new PropertyMetadata(double.Parse("0", CultureInfo.InvariantCulture), CurrentValueChangedCallback));

        private static void CurrentValueChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (UpDownControl)dependencyObject;
            instance.SetStateAndColorArrows((double)e.NewValue);
            if (instance._textValue != null && instance._textAfter != null)
            {
                instance.OnChangedValue((double)e.OldValue, (double)e.NewValue, instance._textValue.Text, instance._textAfter.Text);
            }
        }

        public double CurrentValue
        {
            get { return (double)GetValue(CurrentValueProperty); }
            set
            {
                var oldvalue = (double)GetValue(CurrentValueProperty);
                if (!oldvalue.Equals(value))
                {
                    if (LoopValues)
                    {
                        if (value > MaxValue)
                        {
                            value = MinValue;
                        }
                        if (value < MinValue)
                        {
                            value = MaxValue;
                        }
                    }
                    else
                    {
                        if (value > MaxValue)
                        {
                            value = MaxValue;
                        }
                        if (value < MinValue)
                        {
                            value = MinValue;
                        }
                    }
                    SetValue(CurrentValueProperty, value);
                }
            }
        }

        private void SetStateAndColorArrows(double value)
        {
            if (!IsEnabled)
            {
                _arrright.Fill = _defaultArrowDisabledColor;
                _arrLeft.Fill = _defaultArrowDisabledColor;
                return;
            }
            if (value >= MaxValue)
            {
                if (_arrright != null && _bntright != null)
                {
                    _arrright.Fill = ArrowDisabledColor;
                    _bntright.IsEnabled = false;
                }
            }
            else
            {
                if (_arrright != null && _bntright != null)
                {
                    _arrright.Fill = ArrowColor;
                    _bntright.IsEnabled = true;
                }
            }
            if (value <= MinValue)
            {
                if (_arrLeft != null && _bntLeft != null)
                {
                    _arrLeft.Fill = ArrowDisabledColor;
                    _bntLeft.IsEnabled = false;
                }
            }
            else
            {
                if (_arrLeft != null && _bntLeft != null)
                {
                    _arrLeft.Fill = ArrowColor;
                    _bntLeft.IsEnabled = true;
                }
            }
        }

        public static readonly DependencyProperty TextAfterValueProperty =
            DependencyProperty.Register("TextAfterValue", typeof(string), typeof(UpDownControl),
                                new PropertyMetadata(default(string), null));

        public string TextAfterValue
        {
            get { return (string)GetValue(TextAfterValueProperty); }
            set { SetValue(TextAfterValueProperty, value); }
        }

        public static readonly DependencyProperty ControlBackgroundProperty =
        DependencyProperty.Register("ControlBackground", typeof(Brush), typeof(UpDownControl), new PropertyMetadata(null, ControlBackgroundChangedCallback));

        private static void ControlBackgroundChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (UpDownControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.ControlBackground = DefautBackground;
            }
            instance.Background = instance.ControlBackground;
        }

        public Brush ControlBackground
        {
            get { return (Brush)GetValue(ControlBackgroundProperty); }
            set { SetValue(ControlBackgroundProperty, value); }
        }

        public static readonly DependencyProperty LoopValuesProperty =
             DependencyProperty.Register("LoopValues", typeof(bool), typeof(UpDownControl),
                        new PropertyMetadata(false, null));

        public bool LoopValues
        {
            get { return (bool)GetValue(LoopValuesProperty); }
            set { SetValue(LoopValuesProperty, value); }
        }


    }
}